fix: skip NATS connection when URL is empty - #143
Conversation
Reviewer's GuideEnsure the NATS producer constructor safely no-ops when given an empty URL, and add a regression test to verify it does not establish a NATS connection or enable global publishing in that case. Sequence diagram for NewNatsProducer behavior with empty URLsequenceDiagram
participant Caller
participant NewNatsProducer
participant NATS as nats
Caller->>NewNatsProducer: NewNatsProducer(url, natsGlobalEnabled, natsGlobalEvents, loggerWrapper)
alt [strings.TrimSpace(url) == ""]
NewNatsProducer-->>Caller: &natsProducer{conn:nil, natsGlobalEnabled:false, natsGlobalEvents:nil}
else [strings.TrimSpace(url) != ""]
NewNatsProducer->>NATS: Connect(url)
NATS-->>NewNatsProducer: *nats.Conn
NewNatsProducer-->>Caller: &natsProducer{conn:*nats.Conn, natsGlobalEnabled:natsGlobalEnabled}
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In the empty-URL branch, consider retaining the provided natsGlobalEvents slice instead of forcing it to nil to avoid surprising callers that may rely on the configured event list even when the producer is disabled.
- It may be helpful to log a clear warning when the NATS URL is empty so that operators can distinguish between an intentional configuration (disabled producer) and a misconfiguration.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the empty-URL branch, consider retaining the provided natsGlobalEvents slice instead of forcing it to nil to avoid surprising callers that may rely on the configured event list even when the producer is disabled.
- It may be helpful to log a clear warning when the NATS URL is empty so that operators can distinguish between an intentional configuration (disabled producer) and a misconfiguration.
## Individual Comments
### Comment 1
<location path="pkg/events/nats/nats_producer_test.go" line_range="5" />
<code_context>
+
+import "testing"
+
+func TestNewNatsProducerDoesNotConnectWithoutURL(t *testing.T) {
+ producer, ok := NewNatsProducer(" ", false, nil, nil).(*natsProducer)
+ if !ok {
</code_context>
<issue_to_address>
**suggestion (testing):** Cover both empty and whitespace-only URLs, ideally via table-driven subtests
This test only covers the whitespace-only case (" "). Since the behavior should be the same for both empty and whitespace-only URLs, please either add a separate test for "" or convert this into a table-driven test with subtests for "" and " " (and any other relevant variants) to ensure we’re protected against regressions for all "empty" URL forms.
</issue_to_address>
### Comment 2
<location path="pkg/events/nats/nats_producer_test.go" line_range="10-13" />
<code_context>
+ if !ok {
+ t.Fatal("constructor returned an unexpected producer type")
+ }
+ if producer.conn != nil {
+ t.Fatal("NATS connection must stay nil when NATS_URL is empty")
+ }
+ if producer.natsGlobalEnabled {
+ t.Fatal("NATS global publishing must stay disabled without a URL")
+ }
</code_context>
<issue_to_address>
**suggestion (testing):** Also assert on `natsGlobalEvents` to fully document the constructor behavior when URL is empty
Since the constructor also sets `natsGlobalEvents` to nil when no URL is provided, add an assertion like `if producer.natsGlobalEvents != nil { t.Fatal("natsGlobalEvents must be nil when NATS_URL is empty") }` so the test fully captures the producer’s expected state in this scenario.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| import "testing" | ||
|
|
||
| func TestNewNatsProducerDoesNotConnectWithoutURL(t *testing.T) { |
There was a problem hiding this comment.
suggestion (testing): Cover both empty and whitespace-only URLs, ideally via table-driven subtests
This test only covers the whitespace-only case (" "). Since the behavior should be the same for both empty and whitespace-only URLs, please either add a separate test for "" or convert this into a table-driven test with subtests for "" and " " (and any other relevant variants) to ensure we’re protected against regressions for all "empty" URL forms.
| if producer.conn != nil { | ||
| t.Fatal("NATS connection must stay nil when NATS_URL is empty") | ||
| } | ||
| if producer.natsGlobalEnabled { |
There was a problem hiding this comment.
suggestion (testing): Also assert on natsGlobalEvents to fully document the constructor behavior when URL is empty
Since the constructor also sets natsGlobalEvents to nil when no URL is provided, add an assertion like if producer.natsGlobalEvents != nil { t.Fatal("natsGlobalEvents must be nil when NATS_URL is empty") } so the test fully captures the producer’s expected state in this scenario.
Merges the Athene fork, which sits 11 commits ahead of the same upstream base and had already solved — in production — the three problems this fork was working through, plus a batch of upstream PRs we had not picked up. Only pkg/whatsmeow/service/whatsmeow.go conflicted (6 hunks); the other 63 files merged cleanly. Every conflict was resolved in favour of the incoming version, because each one was a place where both forks fixed the same bug and theirs is the one we chose to keep: - Connection leak: their getAuthContainer/sharedAuthContainer replaces our storeContainer/storeContainerHolder. Same fix, but theirs is a backport of upstream PR evolution-foundation#117, so it will converge with upstream instead of conflicting when that PR merges. It also caps SQLite at MaxOpenConns(1), which ours did not. - Reconnection: their runtime supervisor replaces our EnableAutoReconnect=true. Ours stopped the automatic disconnect loop but left ReconnectClient reachable from the API endpoint and the send retry, so those paths could still spawn a second client. Their runtime token refuses to start a second runtime for an instance from any path, waits for the previous goroutine to exit, deduplicates concurrent reconnects, backs off exponentially with jitter, and probes to confirm the reconnect actually connected. - Shared maps: ClientMapsMu closes the "fatal error: concurrent map writes" hole, which was still open on our side and kills the whole process rather than panicking a single request. Also arriving with the merge: upstream PRs evolution-foundation#149, evolution-foundation#135, evolution-foundation#34, evolution-foundation#100, evolution-foundation#143, evolution-foundation#137, evolution-foundation#120, evolution-foundation#130, evolution-foundation#151 and evolution-foundation#122; multi-webhook fan-out; POST /send/product; POST /user/savecontact; GET /server/stats and the dashboard; the whatsmeow update to 20260721; and removal of the 61 MB compiled binary that was tracked in git. Our GHCR workflow did not conflict and is preserved. store_container_test.go was adapted to the incoming implementation: it now exercises getAuthContainer, asserts against their MaxOpenConns of 20, and resets sharedAuthContainer between tests, since that container is a package-level global and would otherwise carry between tests and invalidate the pg_stat_activity measurements. It is kept alongside their auth_container_retry_test.go because it measures something theirs does not: the connection count the server actually sees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
Related Issue
Closes #(issue_number)
Type of Change
Testing
Screenshots (if applicable)
Checklist
Additional Notes
Summary by Sourcery
Skip establishing a NATS connection when no URL is provided and ensure producer is disabled in that case.
Bug Fixes:
Tests: